home *** CD-ROM | disk | FTP | other *** search
/ Over 1,000 Windows 95 Programs / Over 1000 Windows 95 Programs (Microforum) (Disc 1).iso / 0767 / dwinsock.pas < prev    next >
Pascal/Delphi Source File  |  1995-07-17  |  33KB  |  1,242 lines

  1. {--------------------------------------------------------------
  2.     WinSock component for Borland Delphi.
  3.  
  4.     This was edited using hard tabs every 2nd position.
  5.     Options/Environment/EditorOptions/TabStops = "3 5"
  6.  
  7.     (C) 1995 by Ulf S÷derberg, ulfs@sysinno.se
  8.               Marc Palmer,   marc@landscap.demon.co.uk
  9.                             Keith Hawes,   khawes@ccmail.com
  10.  
  11.     -- History --
  12.         V1.0        950404        US        First release.
  13.         V1.1        950407        US        Corrected TServerSocket bug.
  14.         V1.2        950410        US        Added Address property to server.
  15.         V1.3        950420        MP        Added bitmaps to components,
  16.                                                         added CloseDown procedure to server,
  17.                                                         Added AfterDisconnect and BeforeDisconnect
  18.                                                         properties. Stopped Server accepting
  19.                                                         >MAXCONN connections.
  20.                         950421        US        Replaced TClientEvent and TServerEvent
  21.                                                         with TSocketEvent which passes a TSocket
  22.                                                         reference instead of connection id.
  23.                                                         Also changed TClientSocket.Open and
  24.                                                         TServerSocket.Listen to take one more
  25.                                                         argument which is of type TSocketClass.
  26.                                                         The creation of FConn for TClientSocket
  27.                                                         and FConns array for TServerSocket is
  28.                                                         now done in the Open and Listen procedures
  29.                                                         when you know what kind of socket you want.
  30.                         950421        MP        Patched the whole mess together! Also moved
  31.                                                         the common properties (On from Client+server into
  32.                                                         the TSockCtrl base.
  33.                         950425        MP        Numerous changes to make Info notifications
  34.                                                         work better and added a few new ones.
  35.                                                         Introduced timeout handling. Set the TimeOut
  36.                                                         property of the socket classes at design time
  37.                                                         to set how many seconds it will take before a
  38.                                                         timeout is declared. The OnTimeOut event is
  39.                                                         called when this happends. In the handler you
  40.                                                         should call Close. I'm not sure about Server
  41.                                                         handling yet.
  42.                                                         Replaced TServerSocket.FConns array with a
  43.                                                         TSocketList (derived from TList). Incoming
  44.                                                         connections are no longer limited by MAXCONN.
  45.                                                         There is a design-time MaxConnections property
  46.                                                         for limiting incoming connections.
  47.                                                         Added TClientSocket.Options and
  48.                                                         TServerSocket.ClientOptions properties. These
  49.                                                         determine the mask used for the WSAAsyncSelect
  50.                                                         calls to the corresponding sockets.
  51.                         950509      US      TSockCtrl now inherits from TComponent.
  52.                                                         TSockets are deleted from server.FConns on
  53.                                                         close.
  54.                         950711        US        Corrected bug in TSocket.RemoteHost as pointed
  55.                                                         out by Keith Hawes.
  56.                         950712        KH    * Correct nl not being set bugs in several methods
  57.                                                     * moved LookupName and LookupService from TSocket
  58.                                                         to TSockCtrl since they do not need a connected
  59.                                                         socket to function.  This allows the lookup of
  60.                                                         socket and service information before a
  61.                                                         connection is made. Changed Params from Var to
  62.                                                         const.
  63.                                                     * Added LookupNameStr to return the address as a
  64.                                                         string.
  65.                                                     * Fixed bug in LocateService.
  66.                         950713         KH    * Fixed Bug in TServerSocket.CBSockClose.  Need to
  67.                                                         stop the search after finding and removing the
  68.                                                         matching socket.  The loop stop value is set
  69.                                                         only the first time in the loop and deleting an
  70.                                                         item changes the count and a GFP will result.
  71.                                                     * If all items are needed to be checked for
  72.                                                         deletion use a while loop and don't inc(i) if
  73.                                                         a delete takes place to avoide skipping any
  74.                                                         entries.
  75.                         950714        KH    * Moved RecvText and SendText to TSocket's Private
  76.                                                         section.
  77.  
  78.     Parts of this code was inspired by WINSOCK.PAS by Marc B. Manza.
  79. ---------------------------------------------------------------}
  80.  
  81. unit DWinSock;
  82.  
  83. interface
  84.  
  85. uses
  86.     SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  87.     Forms, Dialogs, Buttons;
  88.  
  89. const
  90.     CM_SOCKMSG    = WM_USER+1;
  91.  
  92. {$I winsock.inc }
  93. {$I winsock.if }
  94.  
  95. type
  96.     { DWinSock exception type }
  97.     ESockError = class(Exception);
  98.  
  99.   TAsyncOptionsType = ( csoRead, csoWrite, csoOOB );
  100.   TAsyncOptions = set of TAsyncOptionsType;
  101.  
  102.   TSockCtrl = class;    { Forward declaration }
  103.  
  104.     { TSocket -- socket api wrapper class. }
  105.     TSocket = class(TObject)
  106.   private
  107.         function RecvText : string;
  108.     procedure SendText(const s : string);
  109.     public
  110.         FParent            : TSockCtrl;        { socket owner }
  111.         FSocket            : TSock;                { socket id }
  112.         FAddr                : sockaddr_in;    { host address }
  113.         FConnected    : boolean;
  114.         FBytesSent    : integer;            { bytes sent by last SendBuf call }
  115.  
  116.         constructor Create(AParent : TSockCtrl); virtual;
  117.         destructor Destroy;
  118.  
  119.         procedure FillSocket(var name, addr, service : string; var port : u_short);
  120.  
  121.         function LocalAddress : string;
  122.         function LocalPort : integer;
  123.  
  124.         function RemoteHost : string;
  125.         function RemoteAddress : string;
  126.         function RemotePort : integer;
  127.  
  128.         procedure SetOptions; virtual;
  129.  
  130.         procedure Listen(var name, addr, service : string; port : u_short;
  131.                             nqlen : integer);
  132.         procedure Open(var name, addr, service : string; port : u_short;
  133.                             opts : TAsyncOptions);
  134.         procedure Close;
  135.  
  136.         function Send(var buf; cnt : integer) : integer;
  137.         function Recv(var buf; cnt : integer) : integer;
  138.  
  139.         function InCount : integer;
  140.  
  141.         property BytesSent : integer read FBytesSent;
  142.         property Text : string read RecvText write SendText;
  143.     end;
  144.  
  145.     TSocketClass = class of TSocket;
  146.  
  147.     TSocketList = class (TList)
  148.     protected
  149.         function GetSocket( Index : Integer ) : TSocket;
  150.     public
  151.         property Sockets[ Index : Integer ] : TSocket read GetSocket;
  152.     end;
  153.  
  154.     { Socket info codes }
  155.     { MP 20/04/95 added siInactive - not used yet - obsolete? }
  156.     {    25/04/95 added siConnected, siClosed, siTimedOut }
  157.     TSockInfo = (  siInactive, siLookUp, siConnect, siConnected, siListen,
  158.                                  siRecv, siSend, siClosed, siTimedOut);
  159.  
  160.     {    Define notification events for socket controls. }
  161.     TSockInfoEvent = procedure (Sender : TObject; icode : TSockInfo) of object;
  162.     TSocketEvent = procedure (Sender : TObject; Socket : TSocket) of object;
  163.  
  164.     {    TSockCtrl -- socket control component base class. }
  165.     TSockCtrl = class(TComponent)
  166.     private
  167.         { US 950509 }
  168.         FHWnd                    : HWnd;
  169.  
  170.         {    Event handler references }
  171.         FOnInfo                : TSockInfoEvent;
  172.         { MP 21/4/95 Moved from TClient+TSocket and 2 new properties added }
  173.         FOnDisconnect    : TSocketEvent;
  174.         FOnRead                : TSocketEvent;
  175.         FOnWrite            : TSocketEvent;
  176.         FOnTimeOut        : TSocketEvent;
  177.  
  178.         { MP 25/4/95 New fields to handle timeout + timer event chains }
  179.         FTimerChainParent, FTimerChainChild : TSockCtrl;
  180.         FTimeOutRemaining   :  Integer;
  181.         FTimeOutActive      :  Boolean;
  182.  
  183.         { Design time connection info }
  184.         FHost                    : string;
  185.         FAddress            : string;
  186.         FService            : string;
  187.         FPort                    : u_short;
  188.  
  189.         FConn                    : TSocket;                { Run time connection info }
  190.         FClass                : TSocketClass;        { class of socket beeing used }
  191.         FTimeOut            : integer;                { timeout length in seconds }
  192.  
  193.         { Access functions }
  194.         procedure SetService(const s : string);
  195.         procedure SetHost(const n : string);
  196.         procedure SetAddress(const a : string);
  197.         procedure SetPort(p : u_short);
  198. { MP 25/4/95 }
  199.         procedure SetTimeOut( p : Integer);
  200.  
  201.         { Returns the WinSock.DLL description }
  202.         function GetDescription : string;
  203.  
  204.     protected
  205.         { Protected declarations }
  206.         constructor Create(AOwner : TComponent); override;
  207.         destructor Destroy; override;
  208.         procedure CBSockClose(ASocket : TSocket); virtual;
  209.  
  210.         { US 950509 }
  211.         procedure WndProc(var Message : TMessage);
  212.         procedure OnSockMsg(var Message : TMessage); virtual; abstract;
  213.  
  214.         { MP 25/4/95 }
  215.         procedure TimerEvent( Sender : TObject);
  216.         procedure UseTimer;
  217.         procedure ReleaseTimer;
  218.  
  219.         { MP 25/4/95  New properties }
  220.         property OnTimeOut : TSocketEvent read FOnTimeOut write FOnTimeOut;
  221.         property TimeOut : Integer read FTimeOut write SetTimeOut;
  222.  
  223.     public
  224.         { Public declarations }
  225.         procedure Info(icode : TSockInfo);
  226.         function LocalHost : string;
  227.         function Reverse(var a : string) : string;
  228.  
  229.         {KH 950712}
  230.         function LookupName(const name : string) : in_addr;
  231.         function LookupNameStr(const name : string) : string;
  232.         function LookupService(const service : string) : u_short;
  233.  
  234.  
  235.         property Handle : HWND read FHWnd;    { US 950509 }
  236.         property Conn : TSocket read FConn;
  237.         property Description : string read GetDescription;
  238.  
  239.     published
  240.         { Published declarations }
  241.         property Address : string read FAddress write SetAddress;
  242.         property Port : u_short read FPort write SetPort;
  243.         property Service : string read FService write SetService;
  244.         property OnInfo : TSockInfoEvent read FOnInfo write FOnInfo;
  245.  
  246. { MP 21/4/95  Moved these props from client+server to TSockctrl }
  247.         property OnDisconnect : TSocketEvent read FOnDisconnect write FOnDisconnect;
  248.         property OnRead : TSocketEvent read FOnRead write FOnRead;
  249.         property OnWrite : TSocketEvent read FOnWrite write FOnWrite;
  250.     end;
  251.  
  252.     { Definition of the TClientSocket component class }
  253.     TClientSocket = class(TSockCtrl)
  254.     private
  255.         FOnConnect    : TSocketEvent;
  256.         FOptions    : TAsyncOptions;
  257.     protected
  258.         { Protected declarations }
  259.         procedure OnSockMsg(var Message : TMessage); override;
  260.         procedure CBSockClose(ASocket : TSocket); override;
  261.  
  262.     public
  263.         { Public declarations }
  264.         procedure Open(ASocketClass : TSocketClass);
  265.         procedure Close;
  266.         function Connected : boolean;
  267.  
  268.     published
  269.         { Published declarations }
  270.          constructor Create(AOwner : TComponent); override;
  271.         destructor Destroy; override;
  272.  
  273.         property Host : string read FHost write SetHost;
  274.         property Options : TAsyncOptions read FOptions write FOptions
  275.                                             default [csoRead, csoWrite];
  276.         property OnConnect : TSocketEvent read FOnConnect write FOnConnect;
  277.         property OnTimeOut;
  278.         property TimeOut;
  279.     end;
  280.  
  281.     { Definition of the TServerSocket component class }
  282.     TServerSocket = class(TSockCtrl)
  283.     private
  284.         { Event handler references }
  285.         FOnAccept                : TSocketEvent;
  286.  
  287.         FMaxConns                : Integer;
  288.         FConns                    : TSocketList;
  289.         FSocketClass        : TSocketClass;
  290.  
  291.         { MP 20/4/95 }
  292.         FOptions                : TAsyncOptions;
  293.  
  294.         function GetClient(cid : integer) : TSocket;
  295.         function GetClientCount : integer;
  296.  
  297.         function DoAccept : integer;
  298.  
  299.     protected
  300.         { Protected declarations }
  301.         procedure OnSockMsg(var Message : TMessage); override;
  302.     procedure CBSockClose(ASocket : TSocket); override;
  303.  
  304.     public
  305.         { Public declarations }
  306.          constructor Create(AOwner : TComponent); override;
  307.         destructor Destroy; override;
  308.  
  309.         procedure Listen(nqlen : integer; ASocketClass : TSocketClass);
  310.         procedure Close;
  311.  
  312.         { MP 20/04/95 added CloseDown declaration. Used CloseDown to avoid
  313.         confusion    with winsock's Shutdown }
  314.         procedure CloseDown; { close server and all connections }
  315.         { Return client socket }
  316.         property Client[cid : integer] : TSocket read GetClient; default;
  317.         property ClientCount : Integer read GetClientCount;
  318.  
  319.     published
  320.         { Published declarations }
  321.         property OnAccept : TSocketEvent read FOnAccept write FOnAccept;
  322.         { MP 25/4/95 New property }
  323.         property MaxConnections : Integer read FMaxConns write FMaxConns default 16;
  324.         property ClientOptions : TAsyncOptions read FOptions write FOptions
  325.                                                             default [csoRead, csoWrite];
  326.     end;
  327.  
  328. procedure Register;
  329.  
  330. implementation
  331.  
  332. uses ExtCtrls;
  333.  
  334. { -- $R DWINSOCK}
  335. const
  336.   { MP 20/04/95 Constant used for drawing component at design time }
  337.   dwsBtnBorderWidth = 2;
  338.   TimerUserCount : Integer = 0;
  339.     TimerChainRoot : TSockCtrl = nil;
  340.  
  341. var
  342.     ExitSave     : Pointer;
  343.     bStarted  : boolean;
  344.     nUsers    : integer;
  345.     nWSErr    : integer;
  346.     myVerReqd : word;
  347.   myWSAData : WSADATA;
  348.   Timer     : TTimer;
  349.  
  350. {$I ERROR.INC}
  351.  
  352. function MakeAsyncMask( Options : TAsyncOptions) : Longint;
  353. begin
  354.   Result := 0;
  355.  
  356.   if csoRead in Options then
  357.       Result := FD_READ;
  358.  
  359.   if csoWrite in Options then
  360.       Result := Result or FD_WRITE;
  361.  
  362.   if csoOOB in Options then
  363.       Result := Result or FD_OOB;
  364. end;
  365.  
  366. { StartUp -- See if a Windows Socket DLL is present on the system. }
  367. procedure StartUp;
  368. begin
  369.     if bStarted then exit;
  370.   nUsers := 0;
  371.     myVerReqd:=$0101;
  372.     nWSErr := WSAStartup(myVerReqd,@myWSAData);
  373.     if nWSErr = 0 then
  374.         bStarted := True
  375.     else
  376.         raise ESockError.Create('Can''t startup WinSock');
  377. end;
  378.  
  379. { CleanUp -- Tell Windows Socket DLL we don't need its services any longer. }
  380. procedure CleanUp; far;
  381. begin
  382.     ExitProc := ExitSave;
  383. { MP 25/4/95 Free timer }
  384.   Timer.Free;
  385.     if bStarted then
  386.   begin
  387.      nWSErr := WSACleanup;
  388.      bStarted := false;
  389.     end;
  390. end;
  391.  
  392. function TSocketList.GetSocket( Index : Integer ) : TSocket;
  393. begin
  394.   Result := Items[Index];
  395. end;
  396.  
  397. {--------------------------------------------------------------
  398.     TSocket implementation
  399.  --------------------------------------------------------------}
  400.  
  401. constructor TSocket.Create(AParent : TSockCtrl);
  402. begin
  403.     inherited Create;
  404.   FParent := AParent;
  405.     FSocket := INVALID_SOCKET;
  406.     FAddr.sin_family := PF_INET;
  407.     FAddr.sin_addr.s_addr := INADDR_ANY;
  408.   FAddr.sin_port := 0;
  409.     FConnected := false;
  410.     FBytesSent := 0;
  411. end;
  412.  
  413. destructor TSocket.Destroy;
  414. begin
  415.     if FSocket <> INVALID_SOCKET  then
  416.         CloseSocket(FSocket);
  417.     inherited Destroy;
  418. end;
  419.  
  420. { LocalAddress -- get local address }
  421. function TSocket.LocalAddress : string;
  422. var
  423.     sa : sockaddr_in;
  424.     nl : integer;
  425. begin
  426.     Result := '';
  427.     if FSocket = INVALID_SOCKET then exit;
  428.  nl := SizeOf(sa);
  429.     if getsockname(FSocket, PSockaddr(@sa), @nl) = 0 then
  430.         Result := StrPas(inet_ntoa(sa.sin_addr));
  431. end;
  432.  
  433. { LocalPort -- get local port number }
  434. function TSocket.LocalPort : integer;
  435. var
  436.     sa : sockaddr_in;
  437.     nl : integer;
  438. begin
  439.     Result := 0;
  440.     if FSocket = INVALID_SOCKET then exit;
  441.  nl := SizeOf(sa);
  442.     if getsockname(FSocket, PSockaddr(@sa), @nl) = 0 then
  443.         Result := ntohs(sa.sin_port);
  444. end;
  445.  
  446. { RemoteHost -- get name of connected remote host }
  447. function TSocket.RemoteHost : string;
  448. var
  449.     sa    : sockaddr_in;
  450.   nl    : integer;
  451.     phe : PHostEnt;
  452. begin
  453.     Result := '';
  454.     if not FConnected then exit;
  455.     nl := sizeof(sa);
  456.     { Get connection address info }
  457.     getpeername(FSocket, PSockaddr(@sa), @nl);
  458.     FAddr := sa;
  459.     { Do a reverse lookup to get the host name }
  460.     phe := gethostbyaddr(PChar(@FAddr.sin_addr.s_addr), 4, PF_INET);
  461.     if phe <> nil then
  462.         Result := StrPas(phe^.h_name);
  463. end;
  464.  
  465. { RemoteAddress -- get address of connected remote host }
  466. function TSocket.RemoteAddress : string;
  467. var
  468.     sa : sockaddr_in;
  469.     nl : integer;
  470. begin
  471.     Result := '?';
  472.     if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;
  473.     nl := SizeOf(sa);
  474.     if getpeername(FSocket, PSockaddr(@sa), @nl) = 0 then
  475.         Result := StrPas(inet_ntoa(sa.sin_addr));
  476. end;
  477.  
  478. { RemotePort -- get remote port number }
  479. function TSocket.RemotePort : integer;
  480. var
  481.     sa : sockaddr_in;
  482.     nl : integer;
  483. begin
  484.     Result := 0;
  485.     if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;
  486.     nl := SizeOf(sa);
  487.     if getpeername(FSocket, PSockaddr(@sa), @nl) = 0 then
  488.         Result := ntohs(sa.sin_port)
  489.     else
  490.         Result := 0;
  491. end;
  492.  
  493. { FillSocket -- fill in address and port fields in socket struct }
  494. procedure TSocket.FillSocket(var name, addr, service : string;
  495.                     var port : u_short);
  496. var
  497.     s    : array [1..32] of char;
  498. begin
  499.     { Fill in address field }
  500.     if name <> '' then                        { Host name given }
  501.         begin
  502.             FAddr.sin_addr := FParent.LookupName(name);        {KH 950712}
  503.          addr := StrPas(inet_ntoa(FAddr.sin_addr));
  504.         end
  505.     else if addr <> '' then                { IP address given }
  506.         begin
  507.             FAddr.sin_addr.s_addr := 0;
  508.             if addr <> '0.0.0.0' then    { beware of Trumpet bug! }
  509.                 begin
  510.                     StrPCopy(@s, addr);
  511.                     FAddr.sin_addr.s_addr := inet_addr(@s);
  512.                 end;
  513.         end
  514.     else                                                    { Neither name or address given }
  515.         raise ESockError.Create('No address specified');
  516.  
  517.     { Fill in port number field }
  518.     if service <> '' then
  519.         begin
  520.             FAddr.sin_port := FParent.LookupService(service);  {KH 950712}
  521.                 port := FAddr.sin_port;
  522.      end
  523.   else
  524.         FAddr.sin_port := htons(port);
  525. end;
  526.  
  527. { SetOptions -- set socket options }
  528. procedure TSocket.SetOptions;
  529. begin
  530. end;
  531.  
  532. { Listen -- wait for incoming connection. }
  533. procedure TSocket.Listen(var name, addr, service : string; port : u_short; nqlen : integer);
  534. var
  535.     q, e    : integer;
  536. begin
  537.     if (not bStarted) then
  538.       raise ESockError.Create('WINSOCK not started');
  539.  
  540.     FSocket := DWinsock.socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
  541.     if FSocket = INVALID_SOCKET then
  542.       raise ESockError.Create('Can''t create new socket');
  543.  
  544.   FillSocket(name, addr, service, port);
  545.  
  546.   SetOptions;
  547.  
  548.     if bind(FSocket, Psockaddr(@FAddr), SizeOf(FAddr)) <> 0 then
  549.         begin
  550.            e := WSAGetLastError;
  551.             Close;
  552.             raise ESockError.Create('Bind failed, '+Error(e));
  553.         end;
  554.  
  555.     WSAAsyncSelect(FSocket, FParent.Handle, CM_SOCKMSG, FD_ACCEPT or FD_CLOSE);
  556.  
  557.     if DWinsock.listen(FSocket, q) <> 0 then
  558.     begin
  559.         e := WSAGetLastError;
  560.         if FSocket <> INVALID_SOCKET then
  561.             Close;
  562.         raise ESockError.Create('Listen failed, '+Error(e));
  563.     end else FParent.Info(siListen);
  564. end;
  565.  
  566. {    Open a connection. }
  567. procedure TSocket.Open(var name, addr, service : string; port : u_short;
  568.                                                     opts : TAsyncOptions);
  569. var
  570.     e        : integer;
  571. begin
  572.     if (not bStarted) then
  573.         raise ESockError.Create('WINSOCK not started');
  574.  
  575.     if FConnected then
  576.         raise ESockError.Create('Can''t open an open socket');
  577.  
  578.     FSocket := DWinsock.socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
  579.     if FSocket = INVALID_SOCKET then
  580.         raise ESockError.Create('Can''t create new socket');
  581.  
  582.     FParent.Info(siLookUp);
  583.     { MP 25/4/95 }
  584.   FParent.UseTimer; { start timeout check }
  585.  
  586.   FillSocket(name, addr, service, port);
  587.     { MP 25/4/95 }
  588.   FParent.ReleaseTimer;
  589.  
  590.   SetOptions;
  591.  
  592.     WSAAsyncSelect(FSocket, FParent.Handle, CM_SOCKMSG, MakeAsyncMask(opts) or
  593.                                 FD_CONNECT or FD_CLOSE);
  594.  
  595.     { MP 25/4/95 }
  596.     FParent.UseTimer; { start timeout check }
  597.     FParent.Info(siConnect);
  598.     if connect(FSocket, Psockaddr(@FAddr), SizeOf(FAddr)) <> 0 then
  599.         if WSAGetLastError <> WSAEWOULDBLOCK then
  600.             begin
  601.                 e := WSAGetLastError;
  602.                 if FSocket <> INVALID_SOCKET then
  603.                     Close;
  604.                 raise ESockError.Create('Open failed: ' + Error(e));
  605.             end;
  606. end;
  607.  
  608. procedure TSocket.Close;
  609. begin
  610.     if (not bStarted) or (FSocket = INVALID_SOCKET) then exit;
  611.     FConnected := false;
  612.     closesocket(FSocket);
  613.     FSocket := INVALID_SOCKET;
  614.     FBytesSent := 0;
  615.   FParent.CBSockClose(self);
  616. end;
  617.  
  618. function TSocket.RecvText : string;
  619. var
  620.   n : integer;
  621. begin
  622.     n := Recv(PChar(@Result[1])^, 255);
  623.   Result[0] := char(n);
  624. end;
  625.  
  626. procedure TSocket.SendText(const s : string);
  627. begin
  628.     FBytesSent := Send(PChar(@s[1])^, Length(s));
  629. end;
  630.  
  631. {    Send contents of passed buffer. }
  632. function TSocket.Send(var buf; cnt : integer) : integer;
  633. var
  634.     n : integer;
  635. begin
  636.     Result := 0;
  637.     if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;
  638.     n := DWinsock.send(FSocket, @buf, cnt, 0);
  639.     if n > 0 then
  640.         Result := n
  641.     else if (n = SOCKET_ERROR) and (WSAGetLastError <> WSAEWOULDBLOCK) then
  642.         begin
  643.             Close;
  644.         raise ESockError.Create('Send error');
  645.      end;
  646. end;
  647.  
  648. {    Request that passed buffer be filled with received data. }
  649. function TSocket.Recv(var buf; cnt : integer) : integer;
  650. var
  651.     n : integer;
  652. begin
  653.     Result := 0;
  654.  
  655.     if (FSocket = INVALID_SOCKET) or (not FConnected) then
  656.       raise ESockError.Create('Socket not open');
  657.  
  658.     n := DWinsock.recv(FSocket, @buf, cnt, 0);
  659.     if n > 0 then
  660.         Result := n
  661.   else if (n = SOCKET_ERROR) and (WSAGetLastError <> WSAEWOULDBLOCK) then
  662.       begin
  663.             Close;
  664.             raise ESockError.Create('Recv error');
  665.      end;
  666. end;
  667.  
  668. { InCount -- Get # of bytes in receive buffer }
  669. function TSocket.InCount : integer;
  670. const
  671.     FIONREAD = $40000000 or ((longint(4)) shl 16) or (ord('f') shl 8) or 127;
  672. var
  673.     n        : longint;
  674. begin
  675.     Result := 0;
  676.     if ioctlsocket(FSocket, FIONREAD, n) <> 0 then
  677.       raise ESockError.Create('ioctlsocket error: ' + error(WSAGetLastError));
  678.   Result := n and $ffff;
  679. end;
  680.  
  681. {--------------------------------------------------------------
  682.     TSockCtrl implementation
  683.  --------------------------------------------------------------}
  684.  
  685. { Create -- initalization }
  686. constructor TSockCtrl.Create(AOwner : TComponent);
  687. begin
  688.     inherited Create(AOwner);
  689.     { US 950509 }
  690.   FHWnd := AllocateHWnd(WndProc);
  691.  
  692.     { The control should be visible at design time only.
  693.       At run time, check if the WINSOCK has been started. }
  694.     if not (csDesigning in ComponentState) then
  695.         StartUp;
  696.  
  697.   FHost := '';
  698.   FAddress := '0.0.0.0';
  699.  
  700.   FService := '';
  701.   FPort := 0;
  702.  
  703.     inc(nUsers);
  704. end;
  705.  
  706. { Destroy -- destruction }
  707. destructor TSockCtrl.Destroy;
  708. var
  709.     res : integer;
  710. begin
  711.   ReleaseTimer;
  712.   FConn.Free;
  713.     Dec(nUsers);
  714.   if nUsers <= 0 then
  715.         CleanUp;
  716.     { US 950509 }
  717.     DeallocateHWnd(FHwnd);
  718.     inherited Destroy;
  719. end;
  720.  
  721. { US 950509: WndProc -- trap socket messages. }
  722. procedure TSockCtrl.WndProc(var Message : TMessage);
  723. begin
  724.   with Message do
  725.         case Msg of
  726.             CM_SOCKMSG : OnSockMsg(Message);
  727.       else
  728.       DefWindowProc(FHWnd, Msg, wParam, lParam);
  729.       end;
  730. end;
  731.  
  732. procedure TSockCtrl.CBSockClose(ASocket : TSocket);
  733. begin
  734. end;
  735.  
  736. { MP 25/4/95  Handle the time out timer events
  737.               This gets a bit tricky, because we don't want to keep
  738.                             wasting CPU time if we have already timed out, so we release
  739.               the timer if we time out. This can only be done once the
  740.               other components in the chain have been called.
  741. }
  742. procedure TSockCtrl.TimerEvent( Sender : TObject );
  743. begin
  744.     if Assigned(FTimerChainChild) then
  745.       FTimerChainChild.TimerEvent(Sender);
  746.     if FTimeOutRemaining > 0 then
  747.       Dec(FTimeOutRemaining);
  748.     if FTimeOutRemaining = 0 then
  749.         begin
  750.             ReleaseTimer; { do this NOW in case event handler takes too long! }
  751.             Info(siTimedOut);
  752.             { MP This should actually pass the actual socket in the case of a server }
  753.             if Assigned(FOnTimeOut) then
  754.           OnTimeOut(Self, Conn);
  755.         end;
  756. end;
  757.  
  758. { Info -- call the OnInfo event handler if any. }
  759. procedure TSockCtrl.Info(icode : TSockInfo);
  760. begin
  761.     if Assigned(FOnInfo) then
  762.       FOnInfo(Self, icode);
  763. end;
  764.  
  765. { GetDescription -- return description of WinSock implementation }
  766. function TSockCtrl.GetDescription : string;
  767. begin
  768.     Result := StrPas(myWSAdata.szDescription);
  769. end;
  770.  
  771. { LocalHost -- return name of local host }
  772. function TSockCtrl.LocalHost : string;
  773. var
  774.     sh : array [0..255] of char;
  775. begin
  776.     if not bStarted then
  777.         begin
  778.             Result := '';
  779.             Exit;
  780.         end;
  781.     if gethostname(sh, 255) = 0 then
  782.         Result := StrPas(sh)
  783.     else
  784.         Result := '';
  785. end;
  786.  
  787. { Set host name }
  788. procedure TSockCtrl.SetHost(const n : string);
  789. begin
  790.     FHost := n;
  791.   FAddress := '';
  792. end;
  793.  
  794. { Set host address }
  795. procedure TSockCtrl.SetAddress(const a : string);
  796. begin
  797.     FAddress := a;
  798.   FHost := '';
  799. end;
  800.  
  801. { Set service name }
  802. procedure TSockCtrl.SetService(const s : string);
  803. begin
  804.     FService := s;
  805.   FPort := 0;
  806. end;
  807.  
  808. { Set port number }
  809. procedure TSockCtrl.SetPort(p : u_short);
  810. begin
  811.     FPort := p;
  812.   FService := '';
  813. end;
  814.  
  815. { MP 25/4/95 }
  816. { Set time out delay }
  817. procedure TSockCtrl.SetTimeOut( p : Integer);
  818. begin
  819.   if p < 0 then p := 0; { trap negatives }
  820.     FTimeOut := p;
  821. end;
  822.  
  823. { there is one global timer, and the different controls chain the calls
  824. to the OnTimer event. }
  825. procedure TSockCtrl.UseTimer;
  826. begin
  827.     if (csDesigning in ComponentState) then
  828.       Exit;
  829.  
  830.   if (FTimeOut = 0) or (not Assigned(FOnTimeOut)) then exit;
  831.  
  832.     if not Assigned(Timer) then
  833.         begin
  834.             Timer := TTimer.Create(Self);
  835.             Timer.Interval := 1000;
  836.             Timer.Enabled := True;
  837.         end;
  838.  
  839.     { Add ourselves to the top of the chain }
  840.     FTimerChainChild := TimerChainRoot;
  841.     FTimerChainParent := nil;
  842.     TimerChainRoot := Self;
  843.     Timer.OnTimer := TimerEvent;
  844.     FTimeOutActive := True;
  845.     FTimeOutRemaining := FTimeOut;
  846.     Inc(TimerUserCount);
  847. end;
  848.  
  849. procedure TSockCtrl.ReleaseTimer;
  850. begin
  851.     if (csDesigning in ComponentState) then Exit;
  852.  
  853.     { US 950502 + removed lots of if FTimeOutActive from other places }
  854.     if not FTimeOutActive then Exit;
  855.  
  856.     { remove ourselves from the chain }
  857.     if Assigned(FTimerChainParent) then
  858.         { reinstate previous handler }
  859.         FTimerChainParent.FTimerChainChild := FTimerChainChild
  860.     else
  861.         begin
  862.             if Assigned( FTimerChainChild) then
  863.           Timer.OnTimer := FTimerChainChild.TimerEvent
  864.             else
  865.           Timer.OnTimer := nil;
  866.             TimerChainRoot := FTimerChainChild;
  867.         end;
  868.  
  869.     if Assigned(FTimerChainChild) then
  870.         FTimerChainChild.FTimerChainParent := FTimerChainParent;
  871.  
  872.     Dec(TimerUserCount);
  873.     FTimeOutActive := False;
  874.     if TimerUserCount = 0 then
  875.         begin
  876.             Timer.Enabled := False;
  877.             Timer.Free;
  878.             Timer := nil;
  879.         end;
  880. end;
  881.  
  882. { Reverse -- try to do a reverse lookup }
  883. function TSockCtrl.Reverse(var a : string) : string;
  884. var
  885.     phe    : PHostEnt;
  886.     s        : array[0..31] of char;
  887.     sa    : in_addr;
  888. begin
  889.     StrPCopy(s, a);
  890.     sa.s_addr := inet_addr(s);
  891.     if sa.s_addr = 0 then
  892.         raise ESockError.Create('Can''t do reverse lookup on address 0.0.0.0');
  893.  
  894.     phe := gethostbyaddr(PChar(@sa.s_addr), 4, PF_INET);
  895.     if phe <> nil then
  896.         Result := StrPas(phe^.h_name)
  897.     else
  898.         raise ESockError.Create('Reverse lookup on ' + a + ' failed');
  899. end;
  900.  
  901. { LookupName -- try to look up host name }
  902. function TSockCtrl.LookupName(const name : string) : in_addr;
  903. var
  904.   phe    : PHostEnt;
  905.   pa    : PChar;
  906.      sz    : array [1..64] of char;
  907.   sa    : in_addr;
  908. begin
  909.     StrPCopy(@sz, name);
  910.     phe := gethostbyname(@sz);
  911.     if phe <> nil then
  912.         begin
  913.         { US 950518 fixed h_addr bug }
  914.             pa := phe^.h_addr_list^;
  915.             sa.S_un_b.s_b1:=pa[0];
  916.             sa.S_un_b.s_b2:=pa[1];
  917.             sa.S_un_b.s_b3:=pa[2];
  918.             sa.S_un_b.s_b4:=pa[3];
  919.      Result := sa;
  920.    end
  921.  else
  922.       raise ESockError.Create('Can''t find host ' + name);
  923. end;
  924.  
  925. function TSockCtrl.LookupNameStr(const name : string): string;
  926. begin
  927.   Result := StrPas(inet_ntoa(LookupName(name)));
  928. end;
  929.  
  930. { LookupService -- try to lookup service name }
  931. function TSockCtrl.LookupService(const service : string) : u_short;
  932. var
  933.     ps    : PServEnt;
  934.     proto    : array [1..32] of char;
  935.     name : array [1..64] of char;
  936. begin
  937.     Result := 0;
  938.     StrPCopy(@proto, 'tcp');
  939.     StrPCopy(@name, service);
  940.     ps := getservbyname(@name, @proto);
  941.     if ps <> nil then
  942.         Result := htons(ps^.s_port){ KH 950712 Changed from: Result := ps^.s_port }
  943.     else
  944.         raise ESockError.Create('Can''t find port for service ' + service);
  945. end;
  946.  
  947. {--------------------------------------------------------------
  948.     TClientSocket implementation.
  949.  --------------------------------------------------------------}
  950.  
  951. constructor TClientSocket.Create(AOwner : TComponent);
  952. begin
  953.     inherited Create(AOwner);
  954.   FOptions := [ csoRead, csoWrite ];
  955. end;
  956.  
  957. destructor TClientSocket.Destroy;
  958. begin
  959.     inherited Destroy;
  960. end;
  961.  
  962. procedure TClientSocket.CBSockClose(ASocket : TSocket);
  963. begin
  964. {    FConn.Free;
  965.   FConn := nil;}
  966. end;
  967.  
  968. procedure TClientSocket.Open(ASocketClass : TSocketClass);
  969. begin
  970.     if Connected then
  971.         raise ESockError.Create('Already opened!');
  972.     FConn.Free;
  973.     FConn := ASocketClass.Create(self);
  974.     FConn.Open(FHost, FAddress, FService, FPort, FOptions);
  975. end;
  976.  
  977. procedure TClientSocket.Close;
  978. begin
  979.     { US 950502 }
  980.     if FConn = nil then
  981.       raise ESockError.Create('Not opened!');
  982.      ReleaseTimer;
  983.     FConn.Close;
  984. end;
  985.  
  986. function TClientSocket.Connected : boolean;
  987. begin
  988.     Result := false;
  989.     if FConn <> nil then
  990.       Result := FConn.FConnected;
  991. end;
  992.  
  993. { OnSockMsg -- handle CM_SOCKMSG }
  994. procedure TClientSocket.OnSockMsg(var Message : TMessage);
  995. var
  996.     sock : TSock;
  997.     evt, err : word;
  998. begin
  999.     sock := TSock(Message.wParam);
  1000.     evt := WSAGetSelectEvent(Message.lParam);
  1001.     err := WSAGetSelectError(Message.lParam);
  1002.  
  1003.     case evt of
  1004.         FD_CONNECT:
  1005.             begin
  1006.                 FConn.FConnected := true;
  1007.                 { MP 25/4/95 }
  1008.            ReleaseTimer;
  1009.                 { MP 950425 Let app know connection is made }
  1010.                 Info(siConnected);
  1011.                 if Assigned(FOnConnect) then
  1012.                     FOnConnect(self, FConn);
  1013.             end;
  1014.  
  1015.         FD_CLOSE:
  1016.             begin
  1017.                 if FConn.FConnected then
  1018.                     begin
  1019.                         { US 950502 user must call xxx.Close method in OnDisconnect event }
  1020.                         if Assigned(FOnDisconnect) then
  1021.                             FOnDisconnect(Self, FConn);
  1022.                         { MP 20/4/95 }
  1023.                ReleaseTimer;
  1024.                         Info(siClosed);
  1025.                     end;
  1026.                 end;
  1027.  
  1028.             FD_OOB: ;
  1029.             FD_READ:
  1030.                 if Assigned(FOnRead) then
  1031.                     FOnRead(Self, FConn);
  1032.  
  1033.             FD_WRITE:
  1034.                 if Assigned(FOnWrite) then
  1035.                     FOnWrite(Self, FConn);
  1036.         end;
  1037.     end;
  1038.  
  1039. {--------------------------------------------------------------
  1040.     TServerSocket functions
  1041.  --------------------------------------------------------------}
  1042.  
  1043. constructor TServerSocket.Create(AOwner : TComponent);
  1044. begin
  1045.     inherited Create( AOwner );
  1046.   FConn := TSocket.Create( Self );
  1047.   FConns := TSocketList.Create;
  1048.   FMaxConns := 16;
  1049.   FOptions := [ csoRead, csoWrite ];
  1050. end;
  1051.  
  1052. destructor TServerSocket.Destroy;
  1053. var
  1054.     i : integer;
  1055. begin
  1056.     for i := 0 to FConns.Count-1 do
  1057.         FConns.Sockets[i].Free;
  1058.   FConns.Free;
  1059.     inherited Destroy;
  1060. end;
  1061.  
  1062. function TServerSocket.GetClient(cid : integer) : TSocket;
  1063. begin
  1064.     Result := FConns[cid];
  1065. end;
  1066.  
  1067. function TServerSocket.GetClientCount : integer;
  1068. begin
  1069.     Result := FConns.Count;
  1070. end;
  1071.  
  1072. procedure TServerSocket.Close;
  1073. begin
  1074.     { US 950502 }
  1075.     ReleaseTimer;
  1076.     FConn.Close;
  1077. end;
  1078.  
  1079. { MP 20/04/95 CloseDown added. Closes all connection sockets and then closes
  1080.   the server socket. Useful for shutting down entire server without destroying
  1081.   the actual server object }
  1082. procedure TServerSocket.CloseDown;
  1083. var
  1084.     i : Integer;
  1085. begin
  1086.     for i := 0 to FConns.Count-1 do
  1087.       FConns.Sockets[i].Close;
  1088.     { MP 20/4/95 }
  1089.     FConn.Close;
  1090.  
  1091.     { US 950502 }
  1092.   ReleaseTimer;
  1093. end;
  1094.  
  1095. { US 950427: CBSockClose }
  1096. procedure TServerSocket.CBSockClose(ASocket : TSocket);
  1097. var
  1098.     i        : integer;
  1099.  
  1100. begin
  1101.     if ASocket = FConn then Exit;  { Server's socket will NOT be in the list }
  1102.     for i := 0 to FConns.Count-1 do
  1103.         if FConns.Sockets[i].FSocket = ASocket.FSocket then
  1104.             begin
  1105.                 FConns.Sockets[i].Free;
  1106.                 FConns.Delete(i);
  1107.                 FConns.Pack;  { ok, not particularly efficient }
  1108.                 Break; { KH 950713 Why Keep going we just removed it }
  1109.             end;
  1110. end;
  1111.  
  1112. { OnSockMsg -- handle CM_SOCKMSG from WINSOCK }
  1113. procedure TServerSocket.OnSockMsg(var Message : TMessage);
  1114. var
  1115.     sock    : TSock;
  1116.     evt    : word;
  1117.   err    : word;
  1118.     cid    : integer;
  1119.  
  1120.     procedure FindConn;
  1121.     var
  1122.         i : integer;
  1123.     begin
  1124.         cid := -1;
  1125.         for i := 0 to FConns.Count-1 do
  1126.             if FConns.Sockets[i].FSocket = sock then
  1127.                 begin
  1128.                     cid := i;
  1129.                     Exit;
  1130.                 end;
  1131.     end;
  1132.  
  1133. begin
  1134.     sock := TSock(Message.wParam);
  1135.     evt := WSAGetSelectEvent(Message.lParam);
  1136.     err := WSAGetSelectError(Message.lParam);
  1137.  
  1138.     case evt of
  1139.         FD_ACCEPT:
  1140.             begin
  1141.                 cid := DoAccept;
  1142.                 if Assigned(FOnAccept) and (cid >= 0) then
  1143.                     FOnAccept( Self, FConns[cid]);
  1144.             end;
  1145.  
  1146.         FD_CLOSE:
  1147.             begin
  1148.                 FindConn;
  1149.                 { MP 18/4/95 changed this from NOT FConns[ to FConns[
  1150.                   I think the logic was slightly erroneous }
  1151.                 if FConns.Sockets[cid].FConnected then
  1152.                     begin
  1153.                         { US 950502 user must call xxx.Close method }
  1154.                         if Assigned(FOnDisconnect) then
  1155.                             FOnDisconnect(Self, FConns.Sockets[cid]);
  1156.  
  1157.                         { MP 25/4/95 }
  1158.                ReleaseTimer;
  1159.                         Info(siClosed);
  1160.                     end;
  1161.             end;
  1162.  
  1163.         FD_OOB: ;
  1164.         FD_READ:
  1165.             begin
  1166.                 FindConn;
  1167.                 if Assigned(FOnRead) then
  1168.                     FOnRead( Self, FConns[cid] );
  1169.             end;
  1170.  
  1171.         FD_WRITE:
  1172.             begin
  1173.                 FindConn;
  1174.                 if Assigned(FOnWrite) then
  1175.                     FOnWrite( Self, FConns[cid] );
  1176.             end;
  1177.     end;
  1178. end;
  1179.  
  1180. function TServerSocket.DoAccept : integer;
  1181. var
  1182.     ts       : TSocket;
  1183.     nl       : integer;
  1184.     cid   : integer;
  1185.  
  1186.     function NewConn : integer;
  1187.     begin
  1188.      Result := FConns.Add( FSocketClass.Create(Self) );
  1189.     end;
  1190.  
  1191. begin
  1192.     Result := -1;
  1193. { MP 25/4/95 - Do not accept any more than FMaxConns connections.
  1194.   Should we do something to let the client know? Like accept and then
  1195.   close straight away ? }
  1196.   if FConns.Count >= FMaxConns then Exit;
  1197.  
  1198.     cid := NewConn;
  1199.     ts := FConns[cid];
  1200.     nl := sizeof(sockaddr_in);
  1201.     ts.FSocket := accept(FConn.FSocket, PSockaddr(@ts.FAddr), @nl);
  1202.     if ts.FSocket <> INVALID_SOCKET then
  1203.         begin
  1204.             WSAAsyncSelect(ts.FSocket, Handle, CM_SOCKMSG, MakeAsyncMask(FOptions) or
  1205.                                         FD_CLOSE);
  1206.             ts.FConnected := True;
  1207.             Result := cid;
  1208.         end;
  1209. end;
  1210.  
  1211. procedure TServerSocket.Listen(nqlen : integer; ASocketClass : TSocketClass);
  1212. var
  1213.     i    : integer;
  1214. begin
  1215.     FSocketClass := ASocketClass;
  1216.     FConn.Listen(FHost, FAddress, FService, FPort, nqlen);
  1217. end;
  1218.  
  1219. {    Register our components. }
  1220. procedure Register;
  1221. begin
  1222.     RegisterComponents('Samples', [TClientSocket]);
  1223.     RegisterComponents('Samples', [TServerSocket]);
  1224. end;
  1225.  
  1226. {$I winsock.imp }
  1227.  
  1228. {--------------------------------------------------------------
  1229.     Unit initialization code.
  1230.  --------------------------------------------------------------}
  1231.  
  1232. initialization
  1233.     bStarted := False;
  1234.     Timer := nil;
  1235.     ExitSave := ExitProc;
  1236.   ExitProc := @CleanUp;
  1237. end.
  1238.  
  1239.  
  1240.  
  1241.  
  1242.